﻿using System;
using System.IO;
using System.ServiceModel.Description;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using UFIDA.U9.Cust.Pub.WS.ProxyService.Json;
using UFIDA.U9.WSS.ProxyService.AccessProxy;
using UFIDA.U9.WSS.ProxyService.Behavior.WebHttp.Base;
using UFIDA.U9.WSS.ProxyService.Exceptions;
using UFIDA.U9.WSS.ProxyService.Json;
using UFIDA.U9.WSS.ProxyService.Models;
using UFSoft.UBF;

namespace UFIDA.U9.WSS.ProxyService.Behavior.WebHttp
{
    public class ContextProxyServiceDispatchFormatter : NewtonsoftJsonDispatchFormatter
    {
        private const string ProxyPropertyName = "proxy";
        private const string DataPropertyName = "data";
        public const string ContextPropertyName = "context";
        private readonly string _accessGroup;
        private readonly bool _accessProxy;
        private readonly bool _contextNeedUserPassword;
        private readonly bool _useDataContract;

        public ContextProxyServiceDispatchFormatter(OperationDescription operation, bool useDataContract = false,
            bool accessProxy = false, string accessGroup = "", bool contextNeedUserPassword = false) : base(operation)
        {
            _useDataContract = useDataContract;
            _accessProxy = accessProxy;
            _accessGroup = accessGroup;
            _contextNeedUserPassword = contextNeedUserPassword;
        }

        protected override void ParseParameters(byte[] rawBody, object[] parameters)
        {
            ContextInfo contextInfo = null;
            Type proxyType = null;
            object proxyObject = null;
            using (MemoryStream ms = new MemoryStream(rawBody))
            {
                using (StreamReader sr = new StreamReader(ms))
                {
                    IContractResolver resolver = new ProxyBaseContractResolver(_useDataContract);
                    JsonSerializer serializer = JsonHelper.GetJsonSerializer(resolver);
                    JsonReader reader = new JsonTextReader(sr);
                    reader.Read();
                    if (reader.TokenType != JsonToken.StartObject)
                    {
                        throw new WSException(ExceptionCodes.ServiceRequestDataFormatException,
                            "input needs to be wrapped in an object");
                    }

                    reader.Read();
                    while (reader.TokenType == JsonToken.PropertyName)
                    {
                        string parameterName = reader.Value as string;
                        parameterName = string.IsNullOrEmpty(parameterName) ? string.Empty : parameterName.ToLower();
                        reader.Read();
                        switch (parameterName)
                        {
                            case ContextPropertyName:
                                try
                                {
                                    contextInfo =
                                        serializer.Deserialize(reader, typeof(ContextInfo)) as ContextInfo;
                                }
                                catch (Exception ex)
                                {
                                    Logger.Error(ex);
                                    throw new WSException(ExceptionCodes.ServiceRequestDataFormatException,
                                        string.Format("{0} property is incorrect", ContextPropertyName),
                                        ex);
                                }

                                if (contextInfo == null)
                                    throw new WSException(
                                        ExceptionCodes.ProxyServiceContextDataIsIncorrect,
                                        "context is incorrect");

                                //上下文验证
                                contextInfo.ValidateAndInit(_contextNeedUserPassword);
                                break;
                            case ProxyPropertyName:
                                string proxyString = reader.Value as string;
                                if (string.IsNullOrWhiteSpace(proxyString))
                                    throw new WSException(ExceptionCodes.ProxyServiceProxyPropertyIsEmpty,
                                        "proxy is empty");
                                //Proxy是否需要授权
                                if (_accessProxy)
                                {
                                    bool isAccess = AccessProxyManager.Instance.IsAccess(_accessGroup, proxyString);
                                    if (!isAccess)
                                        throw new WSException(ExceptionCodes.ProxyServiceProxyAccessDenied,
                                            "proxy access denied");
                                }

                                try
                                {
                                    proxyType = TypeManager.TypeLoader.LoadType(proxyString);
                                }
                                catch (Exception ex)
                                {
                                    Logger.Error(ex);
                                    throw new WSException(ExceptionCodes.ProxyServiceProxyPropertyIsIncorrect,
                                        "proxy is incorrect", ex);
                                }

                                if (proxyType == null)
                                    throw new WSException(ExceptionCodes.ProxyServiceProxyPropertyIsIncorrect,
                                        "proxy is incorrect");
                                break;
                            case DataPropertyName:
                                if (proxyType == null)
                                    throw new WSException(ExceptionCodes.ProxyServiceProxyPropertyIsIncorrect,
                                        "proxy is incorrect");
                                try
                                {
                                    proxyObject = serializer.Deserialize(reader, proxyType);
                                }
                                catch (Exception ex)
                                {
                                    Logger.Error(ex);
                                    throw new WSException(ExceptionCodes.ServiceRequestDataFormatException,
                                        string.Format("{0} property is incorrect", DataPropertyName), ex);
                                }

                                break;
                            default:
                                reader.Skip();
                                break;
                        }

                        reader.Read();
                    }

                    reader.Close();
                    sr.Close();
                    ms.Close();
                }
            }

            parameters[0] = contextInfo;
            parameters[1] = proxyObject;
        }

        protected override IContractResolver GetSerializeReplyContractResolver()
        {
            return new ProxyBaseContractResolver(_useDataContract);
        }
    }
}